#!/bin/sh
#
# Volume check service
#

. /etc/utils.sh

sata_blkdev=$(find_sata_blkdev)
platform=$(cat /etc/platform)

# 'data+ubi' partition
DATA_MTD_NAME='"data+ubi"'
DATA_MTD_NO=

DRM0_MTD_NAME='"drmregion0"'
DRM0_MTD_NO=
DRM1_MTD_NAME='"drmregion1"'
DRM1_MTD_NO=

# 'rootfs' on /dev/ubi0
# 'data+ubi' on /dev/ubi1
DATA_UBI_NO=1

# eMMC support
DATA_MMC_NAME="data+ext4"
ATV_DATA_MMC_NAME="userdata"
ATV_HWCFG_MMC_NAME="hwcfg"
DRM_FS_NAME="drmfs1"
DRM_FS_RECOVER_NAME="drmfs2"

USER_NAME=user
CONFIG_NAME=config

CONFIG_SIZE=$((16*1024*1024))	# 16MiB
USER_SIZE=0
BADBLOCK_FILE=/tmp/gpio/ledcontrol/flash_bad_blocks


check_mtd()
{
  local a b c d e
  cat /proc/mtd |
  while read a b c d; do
    # convert 'mtdxx:' to 'xx'
    e=${a%:*}
    [ $d = $1 ] && echo "${e#mtd}"
  done
}

get_mtd_size()
{
  local a b c d e
  cat /proc/mtd |
  while read a b c d; do
    e=${a%:*}
    [ "${e#mtd}" = "$1" ] && printf "%d" 0x${b}
  done
}


check_mmc()
{
  local result=
  sgdisk -p /dev/mmcblk0 >/tmp/mmcpart
  while read number start end size unit code name; do
    if [ "$name" = "$1" ]; then
      result=$number
    fi
  done </tmp/mmcpart
  rm -f /tmp/mmcpart
  [ -n "$result" ] && echo "$result"
}


fail()
{
  echo "$(basename $0):" "$@" >&2
  [ -f $BADBLOCK_FILE ] || echo "" >$BADBLOCK_FILE
}


recover_volume()
{
  # Format MTD device and mount UBI volumes.
  # $1 - MTD device number
  # $2 - UBI device number

  echo "Failed to mount ubifs on mtd$1, formatting..."

  # umount UBI volumes detach UBI devices
  umount_ubifs $2

  # format MTD partition
  ubiformat /dev/mtd$1 -y

  # attach UBI devices and mount UBI volumes
  mount_ubifs $1 $2
}


mount_ubifs()
{
  # Attach MTD device to UBI device.
  # $1 - MTD device number
  # $2 - UBI device number

  #echo "Attach mtd$1 to /dev/ubi$2"
  # If UBI device failed to attach, return 1 to call 'recover_volume()':
  # 1. detach UBI devices
  # 2. erase MTD partition
  # 3. format MTD partition
  # 4. attach UBI devices and mount UBI volumes again
  ubiattach -m $1 -d $2
  if [ $? -eq 0 ]; then
    mount_volumes $1 $2 || return 1
  else
    echo "Failed to attach mtd$1 to /dev/ubi$2"
    return 1
  fi
}


mount_volumes()
{
  # Mount UBI volumes.
  # $1 - MTD device number
  # $2 - UBI device number

  if [ $1 -eq ${DATA_MTD_NO} ]; then
    # For platforms with a single, small MTD partition;
    # (ie gfrg240) create a single /user partition and
    # have /config reside under /user.

    actual_size=$(get_mtd_size $1)
    if [ "${actual_size}" -gt "${CONFIG_SIZE}" ]; then
      # mount UBI 'config' volume
      mount_volume "${CONFIG_NAME}" "$2" "${CONFIG_SIZE}" || return 1

      # mount UBI 'user' volume
      mount_volume "${USER_NAME}" "$2" "${USER_SIZE}" || return 1
    else
      # mount UBI 'user' volume
      mount_volume "${USER_NAME}" "$2" "${USER_SIZE}" || return 1

      # bring /config under /user
      mkdir -p "/${USER_NAME}/${CONFIG_NAME}"
      mount --bind "/${USER_NAME}/${CONFIG_NAME}" "/${CONFIG_NAME}" || return 1
    fi
  fi
}


mount_volume()
{
  # Mount UBI volume.
  # $1 - UBI volume name
  # $2 - UBI device number
  # $3 - UBI volume size

  # make UBI volume
  make_volume $1 $2 $3 || return 1

  # mount UBI volume
  [ ! -d /$1 ] && mkdir -p /$1
  if ! mount -o chk_data_crc -t ubifs ubi$2:$1 /$1; then
    echo "Failed to mount '$1' volume"
    return 1
  fi
}


make_volume()
{
  # Make UBI volume.
  # $1 - UBI volume name
  # $2 - UBI device number
  # $3 - UBI volume size

  # get UBI volume name and remove 'space' characters
  if ubinfo -d $2 -N $1; then
    echo "UBI volume '$1' exists"
    return 0
  fi

  # echo "Make '$1' volume on /dev/ubi$2 (size:$3)"
  if [ $3 = "0" ]; then
    size_args="-m"
  else
    size_args="-s $3"
  fi

  if ! ubimkvol /dev/ubi$2 ${size_args} -N $1; then
    echo "Failed to make '$1' volume"
    return 1
  fi
}


umount_ubifs()
{
  # Umount UBI volumes.
  # $1 - UBI device number

  if [ $1 -eq ${DATA_UBI_NO} ]; then
    [ -d /${USER_NAME} ] && umount /${USER_NAME}
    [ -d /${CONFIG_NAME} ] && umount /${CONFIG_NAME}
  else
    return 1
  fi

  sleep 1
  ubidetach -d $1
}


mount_drmfs_ro()
{
  # TODO(jnewlin): Making this mount rw for a few builds.
  #  The first batch of EVT2 boards has unbound keyboxes so we need
  #  this writable so the keybox gets bound when oregano starts.
  mount -t ext4 -o rw,defaults $1 $2 || return 1
}

mount_hwcfg_ro()
{
  mount -t cramfs -o ro,relatime,barrier=1 $1 $2 || return 1
}

# $1 : primary drmfs
# $2 : secondary drmfs
recover_drmfs()
{
    e2fsck $1 || mkfs.ext4 $1 || return 1
    mkdir -p /tmp/drmfs_primary
    mkdir -p /tmp/drmfs_secondary
    mount -t ext4 -o ro,defaults $2 /tmp/drmfs_secondary || return 1
    mount -t ext4 -o defaults $1 /tmp/drmfs_primary || return 1
    # TODO(jnewlin):  Need to revisit with real keybox name.
    cp /tmp/drmfs_secondary/drm.bin /tmp/drmfs_primary/drm.bin

    umount /tmp/drmfs_primary
    umount /tmp/drmfs_secondary
    mount_drmfs_ro $1 /user/drmfs
}

mount_ext4fs()
{
  # Use ordered mode & disable delayed allocation to enable metadata journaling
  # and ensure data blocks are always written before metadata. Avoids issues
  # with power loss or crashes resulting in zero length files.
  mount -t ext4 -o defaults,noatime,discard,nodelalloc,data=ordered $1 /user || return 1
  [ ! -d /user/config ] && mkdir -p /user/config
  mount --bind /user/config /config || return 1
}

mount_atv_ext4fs()
{
  mount -t ext4 -o defaults,noatime,discard,nodelalloc,data=ordered $1 /atv_userdata || return 1
  mkdir -p /atv_userdata/gfiber/user
  mount --bind /atv_userdata/gfiber/user /user || return 1
  mkdir -p /user/config
  mount --bind /user/config /config || return 1
}

recover_ext4fs()
{
  e2fsck $1 || mkfs.ext4 -E discard -m 2 -L ${DATA_MMC_NAME} $1
  mount_ext4fs $1
}

recover_atv_ext4fs()
{
  e2fsck $1 || mkfs.ext4 -E discard -m 2 -L ${ATV_DATA_MMC_NAME} $1
  mount_atv_ext4fs $1
}


case "$1" in
  start)
    wait-scsi &   # pre-start this so later scripts can use the result

    # Get MTD partition number
    DATA_MTD_NO=$(check_mtd ${DATA_MTD_NAME})
    DRM0_MTD_NO=$(check_mtd ${DRM0_MTD_NAME})
    DRM1_MTD_NO=$(check_mtd ${DRM1_MTD_NAME})

    # Log bbinfo at boot
    BBT=""
    cat /proc/sys/dev/repartition/bbinfo |
    while read a b ; do
      BBT="$BBT$a$b "
    done
    echo "Bad Blocks: $BBT"

    if [ ! -z "${DATA_MTD_NO}" ]; then
      mount_ubifs ${DATA_MTD_NO} ${DATA_UBI_NO} || \
        recover_volume ${DATA_MTD_NO} ${DATA_UBI_NO}
    else
      DATA_MMC_NO=$(check_mmc ${DATA_MMC_NAME})
      if [ -n "${DATA_MMC_NO}" ]; then
        DATA_BLK_NAME=/dev/mmcblk0p${DATA_MMC_NO}
        echo "Found non-ATV partition table ${DATA_MMC_NAME} at ${DATA_BLK_NAME}!"
        if [ -b ${DATA_BLK_NAME} ]; then
          mount_ext4fs ${DATA_BLK_NAME} || recover_ext4fs ${DATA_BLK_NAME}
        fi
      else
        # try if this is a dual-boot Android TV system
        ATV_DATA_MMC_NO=$(check_mmc ${ATV_DATA_MMC_NAME})
        if [ -n "${ATV_DATA_MMC_NO}" ]; then
          ATV_DATA_BLK_NAME=/dev/mmcblk0p${ATV_DATA_MMC_NO}
          echo "Found ATV partition table ${ATV_DATA_MMC_NAME} at ${ATV_DATA_BLK_NAME}!"
          if [ -b ${ATV_DATA_BLK_NAME} ]; then
            mount_atv_ext4fs ${ATV_DATA_BLK_NAME} || \
              recover_atv_ext4fs ${ATV_DATA_BLK_NAME}
          fi
        else
          echo "Error-Could not find either ${DATA_MMC_NAME} or ${ATV_DATA_MMC_NAME}"
        fi
      fi
    fi

    # Fix access permissions on drm partitions
    if [ ! -z "${DRM0_MTD_NO}" ]; then
      chown appclient:appclient /dev/mtd${DRM0_MTD_NO}
      chown appclient:appclient /dev/mtdblock${DRM0_MTD_NO}
    fi
    if [ ! -z "${DRM1_MTD_NO}" ]; then
      chown appclient:appclient /dev/mtd${DRM1_MTD_NO}
      chown appclient:appclient /dev/mtdblock${DRM1_MTD_NO}
    fi

    [ ! -d /user/rw ] && mkdir -p /user/rw
    [ ! -d /user/bsa ] && mkdir -p /user/bsa
    [ ! -d /user/netflix ] && mkdir -p /user/netflix
    [ ! -d /user/vudu ] && mkdir -p /user/vudu
    [ ! -d /user/drm ] && mkdir -p /user/drm
    [ ! -d /user/widevine ] && mkdir -p /user/widevine
    [ ! -d /user/ytlb ] && mkdir -p /user/ytlb
    [ ! -d /tmp/vudu ] && mkdir -p /tmp/vudu
    [ ! -d /tmp/oprofile ] && mkdir -p /tmp/oprofile

    if has-drm-fs; then
      # create folder for SAGE usage tables if not present
      [ ! -d /user/widevine/brcm_sage ] && mkdir -p /user/widevine/brcm_sage && chmod 777 /user/widevine/brcm_sage
      [ ! -d /user/drmfs ] && mkdir -p /user/drmfs
      ATV_HWCFG_MMC_NO=$(check_mmc ${ATV_HWCFG_MMC_NAME})
      DRMFS_MOUNTED=0 # not mounted yet
      if [ -n "${ATV_HWCFG_MMC_NO}" ]; then
        ATV_HWCFG_BLK_NAME=/dev/mmcblk0p${ATV_HWCFG_MMC_NO}
        echo "Found ATV partition table ${ATV_HWCFG_MMC_NAME} at ${ATV_HWCFG_BLK_NAME}!"
        if [ -b ${ATV_HWCFG_BLK_NAME} ]; then
          if mount_hwcfg_ro ${ATV_HWCFG_BLK_NAME} /user/drmfs; then
            DRMFS_MOUNTED=1
          else
            echo "failed mounting ${ATV_HWCFG_BLK_NAME}"
          fi
        fi
      fi
      if [ ${DRMFS_MOUNTED} -eq 0 ]; then
        DRM_FS_MMC_NO=$(check_mmc ${DRM_FS_NAME})
        DRM_FS_RECOVER_MMC_NO=$(check_mmc ${DRM_FS_RECOVER_NAME})
        DRM_FS_BLK_NAME=/dev/mmcblk0p${DRM_FS_MMC_NO}
        DRM_FS_RECOVER_BLK_NAME=/dev/mmcblk0p${DRM_FS_RECOVER_MMC_NO}
        if [ -b ${DRM_FS_BLK_NAME} ]; then
          if ! mount_drmfs_ro ${DRM_FS_BLK_NAME} /user/drmfs; then
            echo "DRMFS is corrupt, attempting to recover keybox..."
            if ! recover_drmfs ${DRM_FS_BLK_NAME} ${DRM_FS_RECOVER_BLK_NAME}; then
              echo "DRMFS is corrupted and not recoverable."
            fi
          fi
        fi
      fi
    fi

    rm -rf /user/diag/diagdb.bin /user/diag/*.tmp /user/diag/log

    # /config must be writeable by non-root
    chown bin.sys /config
    chmod 775 /config

    # Application data must be owned by appclient
    chown -R appclient.appclient /user/netflix
    chown -R appclient.appclient /user/vudu
    chown -R appclient.appclient /user/drm
    chown -R appclient.appclient /user/widevine
    chown -R appclient.appclient /user/ytlb
    chown -R appclient.appclient /tmp/vudu

    # Application user must be able to write kmsg with logos
    chmod 622 /dev/kmsg

    if [ -e "$sata_blkdev" ]; then
      (
        smartctl -A "$sata_blkdev"
        smartctl -t short "$sata_blkdev"
        sleep 60
        smartctl -l selftest "$sata_blkdev"
      ) 2>&1 | logos smartctl &
    fi

    if [ -e /config/DEBUG ]; then
      echo "Enabling DEBUG mode because /config/DEBUG is set." >&2
      echo >/tmp/DEBUG
    fi
    ;;
  stop)
    # Get MTD partition number
    DATA_MTD_NO=$(check_mtd ${DATA_MTD_NAME})

    umount_ubifs ${DATA_UBI_NO}
    ;;
  restart|reload)
    ;;
  *)
    echo "Usage: $0 {start|stop|restart}" >&2
    exit 1
esac
